#include "StdAfx.h"
#include "ApproachSearcher.h"


CSingleBodyApproachSearcher::CSingleBodyApproachSearcher(ISimpleIntegratorInterface * pIntegrator, IGetCoord * pEphem, eBodies nBodyNum, CAsteroidOrbitParam::Ptr pAsteroidOrbitParam)
: m_nBodyNum(nBodyNum)
, m_dDetectDist(0.3) // 0.2 au
, m_dSearchAccuracy(1.0e-6) 
, m_pIntegrator(pIntegrator)
, m_pEphem(pEphem)
, m_pAsteroidOrbitParam(pAsteroidOrbitParam)
{
	m_dPrevDist = m_dDetectDist * 10.0;
	m_bWasApproach = false;
	m_dPrevTime = 0.0;

	if(nBodyNum >= BODY_COUNT)
		throw CCustomException(_T("body num too big, must be less then %d"), BODY_COUNT);
	if(m_pIntegrator == NULL)
		throw CCustomException(_T("empty rada pointer"));
	if(m_pEphem == NULL)
		throw CCustomException(_T("empty Eptem pointer"));
};

CApproach::Ptr CSingleBodyApproachSearcher::Check(double dTime, const SBody & body)
{
	CApproach::Ptr app;
	if( m_dPrevTime == dTime)
	{
		return app;
	}

	double XDif[3] = {0};
	double curdist = GetDistance(dTime, body, XDif);

	if(curdist > m_dDetectDist)
	{
		m_bWasApproach = false;
		m_dPrevDist = m_dDetectDist * 10.0;
		return CApproach::Ptr();
	}

	//printf("\t\t\t\t %lf %lf %lf %d\n", dTime, curdist, m_dPrevDist- curdist, m_bWasApproach);

	if ( m_dPrevDist < curdist ) 
	{
		if ( !m_bWasApproach ) 
		{
			app = Search(dTime, body, dTime - m_dPrevTime);
			m_bWasApproach = true;
		}
	}
	else
	{
		m_bWasApproach = false;
	}
	m_dPrevDist = curdist;
	m_dPrevTime = dTime;
	return app;
}

CApproach::Ptr CSingleBodyApproachSearcher::Search(double dTime, SBody body, double dStep)
{
	double XDif[3];
	double prevDist = GetDistance(dTime, body, XDif);

	while( fabs(dStep) >= m_dSearchAccuracy)
	{
		m_pIntegrator->SimpleMove(dTime, dTime + dStep, body);
		dTime += dStep;
		double curDist = GetDistance(dTime, body, XDif);
		//printf("%+.15e %+e | %+.12e %+12.8lf\n", dTime, dStep, curDist, (curDist - prevDist) * 150000000);
		if (curDist > prevDist)
			dStep *= -0.1;
		prevDist = curDist;
	}

	{
		dStep *= 10.0;
		m_pIntegrator->SimpleMove(dTime, dTime + dStep, body);
		dTime += dStep;
		double curDist = GetDistance(dTime, body, XDif);
		double diff = fabs(curDist - prevDist);
		_ASSERT(diff < 1.0e-10);
	}

	return CApproach::Ptr(new CApproach(m_nBodyNum, dTime, body, XDif, m_pAsteroidOrbitParam));
}

double CSingleBodyApproachSearcher::GetDistance(double dTime, const SBody & body, double dXdiff[3])
{
	AllBodies bodies;
	m_pEphem->GetCoord(dTime, true, bodies, false);
	const SBody &curBody = bodies.bodies[m_nBodyNum];
	double dDist = 0;
	for(int i = 0; i < sizeof(body.x)/sizeof(body.x[0]); i++)
	{
		dXdiff[i] = body.x[i] - curBody.x[i];
		dDist += dXdiff[i] * dXdiff[i];
	}
	dDist = sqrt(dDist);
	return dDist;
}

// ================================= //

CApproachSearcher::CApproachSearcher(ISimpleIntegratorInterface * pIntegrator, IGetCoord * pEphem, const vector<eBodies> & vecBodysToSearch, CAsteroidOrbitParam::Ptr pAsteroidOrbitParam)
{
	for(size_t i=0; i<vecBodysToSearch.size(); ++i)
	{
		m_listSearcher.push_back(CSingleBodyApproachSearcher(pIntegrator, pEphem, vecBodysToSearch[i], pAsteroidOrbitParam));
	}
}

ListPtrApproachs CApproachSearcher::Check(double dTime, const SBody & body)
{
	ListPtrApproachs listApp;
	ListApprchSch::iterator it = m_listSearcher.begin();
	ListApprchSch::iterator end = m_listSearcher.end();
	for(; it != end; ++it)
	{
		CSingleBodyApproachSearcher & searcher = *it;
		CApproach::Ptr pApp = searcher.Check(dTime, body);
		if( !pApp.isNull() )
		{
			listApp.push_back(pApp);
		}
	}
	listApp.sort(CApproach::CompareApproachesByTime);
	return listApp;
}

